/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include "gui_io.h"
#include "gui_al.h"
#include "error.h"
#include "xml.h"
#include "io.h"
#include "simple.h"
#include "crypt.h"
#include "settings.h"
#include "plugins.h"
#include "login.h"
#include "journal.h"
#include "cryptography.h"
#include "second.h"
#include "calendar.h"
#include "status.h"
#include "dict.h"

char *_day = NULL;





int elog_gui_io_day_list_mode = 0;	//0 means show all
				   //1 means show search results.


int _cal_mode = 0;



int elog_gui_io_open(const char *fileName, struct cal_date *date)
{
  /* this function makes use of goto's to encapsulate the logic */

	 const char *journ = elog_journ_in_current(fileName);
	 if (journ) {
		  if (elog_journ_load_no_last_file(journ)) {
			   elog_err_print("Failed to load journal, can't open entry");
			   return 0;
		  }
		  char *msg;
		  elog_sp_cat(&msg, "Switched to journal:", journ, NULL);
		  elog_status_short_status(msg, 30000, 1);
		  free(msg);
	 }

	 
	 struct dict *current = NULL;
	 //our currently opened document, needed later..

	 /* here's the logic: */
	 if (elog_io_stat(fileName) == ELOG_IO_STAT_FILE) {
	   /* opening file */ 
	   current = elog_crypt_open_dry(fileName);
	   if (current != NULL) {
		 if (dict_get(dict_get(current, "date"), "year") == 0 &&
		 	 dict_get(dict_get(current, "date"), "day")== 0) { //no date
		   if (!date) {
			 goto SET_DATE;
		   } else {
		   	struct dict *curDate = dict_get(current, "date");
			 dict_set(curDate, "month", HEAP_INT(date->month));
			 dict_set(curDate, "year",  HEAP_INT(date->year));
			 dict_set(curDate, "day",  HEAP_INT(date->day));
		   }	 
		   goto CALL_PLGN;
		 } else {
		   goto CALL_PLGN;
		 }
	   } else {//file didn't open successfully:
		 goto NEW_DOC;
 	   }
	 } else {
	   goto NEW_DOC;
	 }
	 
	 /*
	   C89 requires declarations in the beginning of a block.
	   Apparently that only becomes an issue with goto's and labels.
	   So I have to declare these before the labels:
	 */

	 char *realDay;	//taking off the /day at the end, this gives
	 //compatibility with full filenames.
	 char *t_set_date; //a temp variable
	 struct cal_date *date1; //a temp var for SET_DATE
	 struct dict *plgn; //our plgn stuff, done in CALL_PLGN
	struct dict *file_info;

 NEW_DOC:
	 if (current != NULL) elog_wrn_print("Bug in elog_gui_io_open(...)");
	 current = dict_new();
	 dict_set(current, "fileName", CPY_STR(fileName));
	 dict_set_free(current, "fileName", 1);
	 
	 dict_set(current, "created", elog_io_date());
	 dict_set_free(current, "created", 1);
	  
	 
	 dict_set(current, "last_edit", CPY_STR("Never"));
	 dict_set_free(current, "last_edit", 1);
	 
	 dict_set(current, "entry text", CPY_STR(""));
	 dict_set_free(current, "entry text", 1);
	 dict_set_type(current, "entry text", DICT_TYPE_OBJ);

	 file_info = dict_new();
	 {
	 	dict_set(file_info, "encType", 
	 					CPY_STR(elog_journ_current()->settings->encryption));
	 	dict_set_free(file_info, "encType", 1);
	 }
	 dict_set(current, "file_info", file_info);
	 dict_set_type(current, "file_info", DICT_TYPE_DCT);
	 dict_set_free(current, "file_info", 1);
	 dict_unref(file_info);
	 
	 dict_set(current, "subject", CPY_STR(""));
	 dict_set_free(current, "subject", 1);
	 

 SET_DATE: 
	 //char *realDay;
	 //char *t_set_date;
	 elog_sp_cat(&t_set_date, fileName, NULL);
	 *(strrchr(t_set_date, '/')) = '\0';
	 
	 elog_sp_cat(&realDay, t_set_date, NULL);
	 free(t_set_date);
	 
	 
	 date1 =  elog_gui_io_getDate(realDay);
	 free(realDay);
	 struct dict *curDate = dict_new();
	 
	 dict_set(curDate, "month", HEAP_INT(date1->month));
	 dict_set(curDate, "year", HEAP_INT(date1->year));
	 dict_set(curDate, "day", HEAP_INT(date1->day));
	 dict_set(current, "data", curDate);
	 free(date1);
	 

 CALL_PLGN:
	 /*Calling plugins */
	 plgn = dict_new();
	 {
	 	dict_go_fast(plgn);
	 	dict_set(plgn, "fileName", (void *)fileName); //won't free it!
	 	dict_set_type(plgn, "fileName", DICT_TYPE_OBJ); //don't save it
	 	
	 	dict_set(plgn, "document", current);
	 	dict_set_type(plgn, "document", DICT_TYPE_DCT);
	 	dict_set_free(plgn, "document", 1);
	 	dict_go_right(plgn);
	 }

	 elog_plgn_eff_call_all(ELOG_PLGN_SYM_OPEN, plgn);
	 dict_unref(plgn);
	 /*end plugins */
	 elog_set_currentFile(dict_get(current, "fileName"), &current);
	 //we're opening the file once
	 //then plugins can use that, no sense having multiple
	 //plugins processing the same file open!



	 //for calendar
	 elog_gui_io_dispLocation(fileName);
	 return 0;
}


void elog_gui_io_search(void *in)
{
	struct elog_srch_result *res = (struct elog_srch_result *) in;


	char *fileName = NULL;
	char *f = strrchr(res->fileName, '/');
	if (f) {
		elog_sp_cat(&fileName, &(f[1]), NULL);
		fileName[strlen(fileName) - 4] = '\0';
	} else
		fileName = res->fileName;


	if (fileName) {
		int day = elog_sp_stringToShort(fileName);

		//grabbing the number:  .../1.dry, grab 1
		free(fileName);
		elog_gui_io_view_add_result(res->year, res->month,
					    res->day, day);
	} else
		elog_err_print_console
		    ("For some reason, res->fileName was undefined?\n");

}
void elog_gui_io_fileHandler(char *fileHandle)
{
	elog_gui_io_save();	//saves current entry before handling link

	if (fileHandle[0] == 'l' &&
	    fileHandle[1] == 'o' &&
	    fileHandle[2] == 'c' &&
	    fileHandle[3] == 'a' &&
	    fileHandle[4] == 'l' &&
	    fileHandle[5] == ':' &&
	    fileHandle[6] == '/' && fileHandle[7] == '/') {
		char *name;
		elog_sp_cat(&name, elog_set_path(NULL), "/",
			    &fileHandle[8], NULL);
		elog_gui_io_open(name, NULL);
		free(name);
	} else if (fileHandle[0] == 's' &&
		   fileHandle[1] == 'e' &&
		   fileHandle[2] == 'a' &&
		   fileHandle[3] == 'r' &&
		   fileHandle[4] == 'c' &&
		   fileHandle[5] == 'h' && fileHandle[6] == ':') {

		char **token = elog_sp_breakToArr(fileHandle, '/');

		struct dict *srch = dict_new();
		{
			dict_go_fast(srch);
			dict_set(srch, "term", token[1]);
			dict_set(srch, "dir", (void *)elog_set_get_str("path"));
			dict_set(srch, "functor", elog_gui_io_search);
			dict_set_type(srch, "functor", DICT_TYPE_OBJ);
			dict_go_right(srch);
		}
		elog_plgn_eff_call_all(ELOG_PLGN_SYM_SEARCH, srch);

		dict_unref(srch);

		elog_sp_ArrFree(token);
	} else if (fileHandle[0] == 'h' &&
		   fileHandle[1] == 't' &&
		   fileHandle[2] == 't' &&
		   fileHandle[3] == 'p' && fileHandle[4] == ':') {
		const char *browser = elog_set_get_str("BROWSER");
		if (!browser)
			browser = "firefox";
		char *cmd;
		elog_sp_cat(&cmd, browser, " ", &(fileHandle[7]), NULL);
		printf("Command:%s\n", cmd);
		system(cmd);
		free(cmd);
	}

}

struct _gui_io_save
{
  struct elog_crypt_save *object;
  int try;
};
int __elog_gui_io_save(void *p)
{
  struct _gui_io_save *s = (struct _gui_io_save *)p;
  /* the following is retry code... */
  if (s->try) {
	char *msg;
	elog_sp_cat(&msg, "Failed to save entry ", 
					dict_get(s->object->doc, "subject"),
					"\nas file: ", 
					dict_get(s->object->doc, "fileName"), NULL);
	elog_err_print(msg);
	free(msg);
	if (s->try < 2) {
	  usleep(200 * (s->try));
	  elog_sp_cat(&msg, "Trying to save entry ", 
	  			dict_get(s->object->doc, "subject"),
				"again ", NULL);
	  elog_err_print(msg);
	  free(msg);
	} else {
	  char *msg;
	  elog_sp_cat(&msg, "Try to save entry again, ", 
				  dict_get(s->object->doc, "subject"), "?", NULL);
	  if (elog_wrn_ask(msg, ELOG_WRN_ASK_YESNO))
		s->try -= 2;
	  else {
		free(msg);
		return 0;
	  }
	  free(msg);
	}
  }
  s->try += 1;
  return elog_crypt_save_dry(s->object);
}


int elog_gui_io_save()
{
  struct dict *current = NULL;
  elog_set_currentFile(NULL, &current);
  if (!current)
	return 1;

	if (dict_check(current, "fileName", NULL)) {
		elog_err_print("Attempt to save invalid file");
		return 4;	
	}

	const char *journ = elog_journ_in_current(dict_get(current, "fileName"));
	if (journ) {
		char *msg =
		    "You need to switch journals to save the currently opened file.\nWould you like to do so?  If you click yes, you will switch to a different journal and save it.\nIf you click no, you will stay here but the file will remain unsaved!";
		int res = elog_wrn_ask(msg, ELOG_WRN_ASK_YESNO);
		if (res) {
			if (elog_journ_load(journ)) {
				elog_err_print
				    ("Failed to load journal!\n");
				return 3;
			}
		} else
			return 2;
	}



  struct dict *plgn = dict_new();
  {
  	dict_go_fast(plgn);
  	dict_set(plgn, "fileName", (void *)dict_get(current, "fileName"));
  	dict_set_free(plgn, "fileName", 0);
  	
  	dict_set(plgn, "document", current);
  	dict_ref(current);
  	dict_set_type(plgn, "document", DICT_TYPE_DCT);
  	dict_set_free(plgn, "document", 1);
  	dict_go_right(plgn);
  }
  elog_plgn_eff_call_all(ELOG_PLGN_SYM_SAVE, plgn);
  dict_unref(plgn);

  struct elog_crypt_save *save = malloc(sizeof *save);
  save->enc = elog_journ_current()->settings->encryption;
  save->len = strlen(dict_get(current, "entry text"));
  save->txt = elog_sp_toUnsignedChar(dict_get(current, "entry text"), 
  															&(save->len));
  save->doc = current;

	struct _gui_io_save *object = malloc(sizeof *object);
	object->object = save;
	object->try = 0;

  elog_scnd_run_arg(__elog_gui_io_save, "save", object);
  return 0;
}

void elog_gui_io_view_set_result_mode(int mode)
{
	_cal_mode = mode;
}

void elog_gui_io_markCalendar()
{
	switch (_cal_mode) {
	case 0:
		{
			elog_gui_io_view_dir_results();
			break;
		}
	case 1:
		{
			elog_gui_io_view_srch_results();
			break;
		}
	}
}
void elog_gui_io_view_dir_results()
{
	elog_gui_al_clearMarks();



	struct cal_date *date = elog_gui_al_calendarDate(NULL);

	char *year;
	char *month;


	year = elog_sp_shortToString(date->year);
	month = elog_sp_shortToString(date->month);

	const char *path = elog_set_path(NULL);

	char *file;
	elog_sp_cat(&file, path, "/", year, "/", month, NULL);


	free(year);
	free(month);


	char **list = elog_io_dirContents(file);
	free(file);

	int c;
	for (c = 0; list[c] != NULL; ++c) {
		int day = elog_sp_stringToShort(list[c]);
		elog_gui_al_markDate(day);

		free(list[c]);
	}
	free(list);
	free(date);
}

struct cal_date *elog_gui_io_getDate(const char *fileName)
{

	char **token = elog_sp_breakToArr(fileName, '/');

	if (token == NULL)
		return NULL;	//this should never happen.
	int c;
	for (c = 0; token[c] != NULL; ++c);	//intentional

	struct cal_date *date = malloc(sizeof *date);

	date->month = elog_sp_stringToShort(token[c - 2]);
	date->year = elog_sp_stringToShort(token[c - 3]);
	date->day = elog_sp_stringToShort(token[c - 1]);


	elog_sp_ArrFree(token);
	return date;
}
void elog_gui_io_dispLocation(const char *fileName)
{
	char *realDay;		//taking off the /day at the end, this gives
	//compatibility with full filenames.
	int len = strlen(fileName);
	int entry = 0;
	if (fileName[len-1] == 'y' && fileName[len-2] == 'r' &&
		fileName[len-3] == 'd' && fileName[len-4] == '.')
		entry = 1;
	char *t;
	elog_sp_cat(&t, fileName, NULL);
	*(strrchr(t, '/')) = '\0';

	elog_sp_cat(&realDay, t, NULL);


	struct cal_date *date = elog_gui_io_getDate(realDay);

	elog_gui_al_calendarDate(date);

	free(date);
	elog_gui_io_dispDay(realDay);
	free(realDay);
	if (entry) {
		t[strlen(t)] = '/'; //we changed this earlier.
		elog_cal_highlightEntry(t);	
	}
	free(t);
}

void elog_gui_io_dispDay(const char *day)
{
	if (day == NULL)
		day = _day;
	else {
		if (_day != NULL)
			free(_day);
		elog_sp_cat(&_day, day, NULL);	//storing current day
	}

	if (day == NULL)
		return;		//bail no data to use!


	elog_gui_al_clearDay();
	
	char **content = elog_io_dirContents(day);

	if (content == NULL) {
		elog_err_print_console("Warning, day does not exist\n");
		return;
	}


	int c;
	for (c = 0; content[c] != NULL; ++c) {
		if (_cal_mode) {	//do this if we're worried about search results.
			char **list = elog_sp_breakToArr(day, '/');
			int i;
			for (i = 0; list[i]; ++i);
			int year, month, day;
			year = elog_sp_stringToShort(list[i - 3]);
			month = elog_sp_stringToShort(list[i - 2]);
			day = elog_sp_stringToShort(list[i - 1]);
			content[c][strlen(content[c]) - 4] = '\0';
			int n = elog_sp_stringToShort(content[c]);

			content[c][strlen(content[c])] = '.';

			//.../year/month/day/NULL
			//    i-3  i-2   i-1  i
			elog_sp_ArrFree(list);
			if (!
			    (elog_gui_io_view_in_srch_results
			     (year, month, day, n)))
				continue;	//skip this one, it's NOT there.
		}


		char *path;
		elog_sp_cat(&path, day, "/", content[c], NULL);	//full fileName
		struct dict *file =
		    elog_crypt_open_dry_noTxt(path);
		free(path);

		if (!dict_check(file, "handle", "subject", "fileName", NULL) ||
			!dict_check(file, "info", "mood", NULL)) {
			struct elog_io_file *handle = dict_get(file, "handle");

			if (handle) {
				
				elog_io_close(handle);
				dict_remove(file, "handle");
				int mood = *(int *)dict_rget(file, "info", "mood", NULL);
				
				elog_gui_al_addItemToDay(dict_get(file, "subject"),
							 dict_get(file, "fileName"),
							 mood);
			}
		}
		
		if (file) dict_unref(file);
		free(content[c]);
	}
	char *name = elog_sp_shortToString(++c);
	char *path;
	elog_sp_cat(&path, day, "/", name, ".dry", NULL);
	elog_gui_al_addItemToDay("Add A New Entry", path, -1);
	free(name);
	free(path);
	free(content);
}
void elog_gui_io_newEntry(const char *day)
{

	char **content = elog_io_dirContents(day);
	if (content == NULL) {
		char *path;
		elog_sp_cat(&path, day, "/", "1.dry", NULL);
		elog_gui_io_open(path, NULL);
		free(path);
		return;
	}
	int c;
	for (c = 0; content[c] != NULL; ++c);	//intentional

	char *name = elog_sp_shortToString(++c);
	char *path;
	elog_sp_cat(&path, day, "/", name, ".dry", NULL);
	elog_gui_io_open(path, NULL);
	free(name);
	free(path);
	free(content);

}

struct _gui_io_results {
	int val;
	struct _gui_io_results *next;
	struct _gui_io_results *child;
};
struct _gui_io_results *_grandDaddy = NULL;

int elog_gui_io_view_in_srch_results(int year, int month, int day, int n)
{
	struct _gui_io_results *p = _grandDaddy;
	while (p) {
		if (p->val == year) {
			p = p->child;
			while (p) {
				if (p->val == month) {
					p = p->child;
					while (p) {
						if (p->val == day) {
							p = p->child;
							while (p) {
								if (p->
								    val ==
								    n)
									return
									    1;
								p = p->
								    next;
							}
							return 0;
						}
						p = p->next;
					}
					return 0;
				}
				p = p->next;
			}
			return 0;
		}
		p = p->next;
	}
	return 0;
}

void elog_gui_io_view_srch_results()
{
	elog_gui_al_clearMarks();
	struct cal_date *date = elog_gui_al_calendarDate(NULL);
	int year = date->year;
	int month = date->month;
	free(date);
	struct _gui_io_results *p = _grandDaddy;
	while (p) {
		if (p->val == year) {
			p = p->child;
			while (p) {
				if (p->val == month) {
					p = p->child;
					while (p) {
						elog_gui_al_markDate(p->
								     val);
						p = p->next;
					}
					break;
				}
				p = p->next;
			}
			break;
		}
		p = p->next;
	}
}


struct _gui_io_results *_gui_io_result_new()
{
	struct _gui_io_results *ret = malloc(sizeof *ret);
	ret->next = NULL;
	ret->child = NULL;
	ret->val = 0;
	return ret;
}
void elog_gui_io_view_add_result(int year, int month, int day, int n)
{
	struct _gui_io_results *p = _grandDaddy;
	while (p) {
		if (p->val == year) {
			p = p->child;
			while (p) {
				if (p->val == month) {
					p = p->child;
					while (p) {
						if (p->val == day) {
							p = p->child;
							struct
							    _gui_io_results
							    *q = p->next;
							p->next =
							    _gui_io_result_new
							    ();
							p->next->next = q;
							p->next->val = n;
							return;
						}
						if (p->next)
							p = p->next;
						else {
							p->next =
							    _gui_io_result_new
							    ();
							p->next->val = day;
							p->next->child =
							    _gui_io_result_new
							    ();
							p->next->child->
							    val = n;
							return;
						}
					}
				}
				if (p->next)
					p = p->next;
				else {
					p->next = _gui_io_result_new();
					p->next->val = month;
					p->next->child =
					    _gui_io_result_new();
					p->next->child->val = day;
					p->next->child->child =
					    _gui_io_result_new();
					p->next->child->child->val = n;
					return;
				}
			}
		}
		if (p->next)
			p = p->next;
		else {
			p->next = _gui_io_result_new();
			p->next->val = year;
			p->next->child = _gui_io_result_new();
			p->next->child->val = month;
			p->next->child->child = _gui_io_result_new();
			p->next->child->child->val = day;
			p->next->child->child->child =
			    _gui_io_result_new();
			p->next->child->child->child->val = n;
			return;
		}

	}
	if (!p) {
		p = _gui_io_result_new();
		p->val = year;
		p->child = _gui_io_result_new();
		p->child->val = month;
		p->child->child = _gui_io_result_new();
		p->child->child->val = day;
		p->child->child->child = _gui_io_result_new();
		p->child->child->child->val = n;
		_grandDaddy = p;
	}
}

void _gui_io_result_free(struct _gui_io_results *res)
{

	if (res->next)
		_gui_io_result_free(res->next);
	if (res->child)
		_gui_io_result_free(res->child);

	free(res);
}

void elog_gui_io_view_clear_results()
{
	if (_grandDaddy)
		_gui_io_result_free(_grandDaddy);
	_grandDaddy = NULL;
}





void elog_gui_io_open_manual()
{
	const char *browser = elog_set_get_str("BROWSER");
	if (!browser)
		browser = "gnome-open";
	char *call;
	elog_sp_cat(&call, browser, " ", DATADIR,
		    "/ejourn/doc/index.html &", NULL);
	system(call);
	free(call);
}
